import java.util.ArrayList;
//import java.util.Random;
import java.lang.Math;

public class Usuario
{
	public ArrayList<Integer> blocos;
	private String tipo;
	private Sistema sistema;
	private double relogio;
	private double horaSaida;
	private double horaChegada;
	private double horaSeed;
	private double taxa;

	public double getHoraSeed()
	{
		return horaSeed;
	}

	public Usuario(Sistema sistema, String tipo, double taxa)
	{
		blocos = new ArrayList<Integer>(sistema.quantidadeBlocos);
		this.tipo = tipo;
		this.sistema = sistema;
		relogio = sistema.tempoAtual;
		this.taxa = taxa;
		setRelogio();
		sistema.addEvento(new Evento("upload", this, relogio));
		horaSeed = 0;

		if (tipo.equals("publisher"))
		{
			for (int i = 0; i < sistema.quantidadeBlocos; i++)
			{
				blocos.add(i);
			}
		}
	}

	public double getHoraChegada()
	{
		return horaChegada;
	}

	public double getHoraSaida()
	{
		return horaSaida;
	}

	public void setHoraChegada(double horaChegada)
	{
		this.horaChegada = horaChegada;
	}

	public void setHoraSaida(double horaSaida)
	{
		this.horaSaida = horaSaida;
	}

	public Usuario recebeBloco(int bloco)
	{
		blocos.add(bloco);
		if (blocos.size() == sistema.quantidadeBlocos)
		{
			tipo = "seed";
			horaSeed = sistema.tempoAtual;
			sistema.peers.remove(this);
			horaSaida = geraHoraSaida();
			sistema.addEvento(new Evento("saida", this, horaSaida));
			return this;
		}
		return null;
	}

	public double geraHoraSaida()
	{

		if (sistema.gama < 0)
		{
			return sistema.tempoAtual;
		}

		double randomico = sistema.rn.nextDouble();
		while (randomico == 0.0 || randomico == 1.0)
		{
			randomico = sistema.rn.nextDouble();
		}

		return ((-1) * ((Math.log(1 - randomico)) / sistema.gama)) + sistema.tempoAtual;
	}

	public Usuario fazerUpload(ArrayList<Usuario> peers)
	{
		int blocoEscolhido;
		Usuario parceiro;

		parceiro = escolheParceiro(peers);
		if (parceiro == null)
		{
			setRelogio();
			sistema.addEvento(new Evento("upload", this, relogio));
			return null;
		}

		blocoEscolhido = escolheBloco(parceiro);

		if (blocoEscolhido == -1)
		{
			setRelogio();
			sistema.addEvento(new Evento("upload", this, relogio));
			return null;
		}

		setRelogio();
		sistema.addEvento(new Evento("upload", this, relogio));
		return parceiro.recebeBloco(blocoEscolhido);
	}

	public Usuario fazerUpload2(ArrayList<Usuario> peers)
	{
		int blocoEscolhido;
		Usuario parceiro;

		parceiro = escolheParceiro(peers);
		if (parceiro == null)
		{
			setRelogio();
			sistema.addEvento(new Evento("upload", this, relogio));
			return null;
		}

		blocoEscolhido = escolheBloco2(parceiro);

		if (blocoEscolhido == -1)
		{
			setRelogio();
			sistema.addEvento(new Evento("upload", this, relogio));
			return null;
		}

		setRelogio();
		sistema.addEvento(new Evento("upload", this, relogio));
		return parceiro.recebeBloco(blocoEscolhido);
	}

	void setRelogio()
	{
		double randomico = sistema.rn.nextDouble();
		while (randomico == 0.0 || randomico == 1.0)
		{
			randomico = sistema.rn.nextDouble();
		}

		relogio = (-1) * ((Math.log(1 - randomico)) / taxa) + sistema.tempoAtual;
	}

	public int escolheBloco(Usuario parceiro)
	{
		// determinar quais meu parceiro nao tem e eu tenho
		ArrayList<Integer> blocoCandidatos = new ArrayList<Integer>();
		for (Integer bloco : blocos)
		{
			if (!(parceiro.blocos.contains(bloco)))
			{
				blocoCandidatos.add(bloco);
			}
		}

		if (blocoCandidatos.size() == 0) return -1;

		// dentre esses escolher um (random)
		int random = sistema.rn.nextInt();
		while (random < 0)
		{
			random = sistema.rn.nextInt();
		}

		int escolha = random % (blocoCandidatos.size());
		return blocoCandidatos.get(escolha);
	}

	public int escolheBloco2(Usuario parceiro)
	{
		// determinar quais meu parceiro nao tem e eu tenho
		ArrayList<Integer> blocoCandidatos = new ArrayList<Integer>();
		for (Integer bloco : blocos)
		{
			if (!(parceiro.blocos.contains(bloco)))
			{
				blocoCandidatos.add(bloco);
			}
		}

		if (blocoCandidatos.size() == 0) return -1;

		// dentre esses escolher um (rarest)
		int menor = -1;
		int blocoEscolha = -1;
		for (Integer bloco : blocoCandidatos)
		{
			for (Par p : sistema.disponibilidade)
			{
				if (p.bloco == bloco)
				{
					if (p.disponibilidade < menor || menor == -1)
					{
						menor = p.disponibilidade;
						blocoEscolha = p.bloco;
						break;
					}
				}
			}
		}
		
		for (Par p : sistema.disponibilidade)
		{
			if (p.bloco == blocoEscolha)
			{
				p.disponibilidade++;
			}
		}

		return blocoEscolha;
	}

	public Usuario escolheParceiro(ArrayList<Usuario> peers)
	{
		if (peers.size() == 0) return null;
		if (tipo == "peer" && peers.size() == 1) return null;

		// Random rn = new Random();
		int random = sistema.rn.nextInt();
		while (random < 0)
		{
			random = sistema.rn.nextInt();
		}
		int escolha = random % (peers.size());

		while (escolha < 0 || peers.get(escolha) == this)
		{
			escolha = sistema.rn.nextInt() % (peers.size());
		}
		return peers.get(escolha);
	}
}
